// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.ui.web.browser.dom;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.internal.ole.win32.IDispatch;

/**
 * The <code>NodeList</code> class provides the abstraction of an ordered collection of nodes, without defining or
 * constraining how this collection is implemented. <code>NodeList</code> objects in the DOM are live.
 * <p>
 * The items in the <code>NodeList</code> are accessible via an integral index, starting from 0.
 * <p>
 * See also the <a href='http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407'>Document Object Model (DOM) Level 3
 * Core Specification</a>.
 * 
 * @author jliu
 */
public class NodeList extends Node {

    private boolean cached;

    private List<Node> childCache;

    public NodeList(IDispatch dispatch, DocumentContext context) {
        super(dispatch, context);
        cached = false;
        cacheNodes();
    }

    /**
     * Put all children to a cache, this is useful for inquiring.
     */
    private void cacheNodes() {
        int length = getLength();
        childCache = new ArrayList<Node>(length);
        for (int i = 0; i < length; i++) {
            IDispatch disp = getIndexedDispProperty("item", i); //$NON-NLS-1$
            Node node = context.getNode(disp);
            childCache.add(node);
        }
        cached = true;
    }

    public int Release() {
        int ref = super.Release();
        if (ref == 0 && cached) {
            childCache.clear();
            childCache = null;
            cached = false;
        }
        return ref;
    }

    /**
     * Returns the <code>index</code>th item in the collection. If <code>index</code> is greater than or equal to
     * the number of nodes in the list, this returns <code>null</code>.
     * 
     * @param index Index into the collection.
     * @return The node at the <code>index</code>th position in the <code>NodeList</code>, or <code>null</code>
     * if that is not a valid index.
     */
    public Node item(int index) {
        if (cached) {
            return childCache.get(index);
        } else {
            IDispatch disp = getIndexedDispProperty("item", index); //$NON-NLS-1$
            return context.getNode(disp);
        }
    }

    /**
     * The number of nodes in the list. The range of valid child node indices is 0 to <code>length-1</code> inclusive.
     */
    public int getLength() {
        if (cached) {
            return childCache.size();
        }
        return getIntProperty("length"); //$NON-NLS-1$
    }
}
